import axios from 'axios';
import { sessionCache } from '../storage';
import { message } from 'antd';
import type {
	AxiosInstance,
	AxiosResponse,
	AxiosRequestConfig,
	InternalAxiosRequestConfig
} from 'axios';
import { AnyObject } from '@/components/TablePro/type';
import { isEqual } from 'lodash-es';
import jsonbig from 'json-bigint';

// 请求配置类型
interface IRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
	ignoreSource?: boolean;
	loading?: boolean;
	interceptors?: {
		requestSuccessFn?: (config: AxiosRequestConfig) => InternalAxiosRequestConfig;
		requestFailureFn?: (err: any) => any;
		responseSuccessFn?: (res: T) => T;
		responseFailureFn?: (err: any) => any;
	};
}

class Request {
	private instance: AxiosInstance;
	private abortControllerMap: Map<
		string,
		{ controller: AbortController; payload: AnyObject; method: string }
	>;

	constructor(config: IRequestConfig) {
		this.instance = axios.create(config);
		this.abortControllerMap = new Map();

		this.instance.interceptors.request.use(
			(config) => {
				const payload = ['post', 'put', 'patch'].includes(config.method)
					? config.data
					: config.params;
				const prev = this.abortControllerMap.get(config.url);
				if (prev && isEqual(payload, prev.payload) && config.method === prev.method) {
					return Promise.reject({ code: 1000, data: '相同接口, 相同参数', message: 'error' });
				}

				const controller = new AbortController();
				const url = config.url || '';
				config.signal = controller.signal;
				this.abortControllerMap.set(url, { controller, payload, method: config.method });

				config.transformResponse = [(data) => data];

				const token = sessionCache.getItem('token');
				if (token && config.headers) {
					config.headers['Authorization'] = token;
				}
				return config;
			},
			(err) => new Error(err)
		);
		this.instance.interceptors.response.use(
			(res) => {
				res.data = jsonbig({ storeAsString: true }).parse(res.data);

				// 全局响应成功 - 返回真正的接口数据(data)
				// if (res.data.code === 200) {
				// 	return res.data;
				// } else {
				// 	return Promise.reject(res.data);
				// }

				if (res.data.code === 401) {
					message.error('登录过期');
				}

				const url = res.config.url || '';
				this.abortControllerMap.delete(url);
				return res.data;
			},
			(err) => {
				if (err.code !== 1000) {
					message.error(err.message);
				}
				return Promise.reject(err);
			}
		);

		this.instance.interceptors.request.use(
			config.interceptors?.requestSuccessFn,
			config.interceptors?.requestFailureFn
		);
		this.instance.interceptors.response.use(
			config.interceptors?.responseSuccessFn,
			config.interceptors?.responseFailureFn
		);
	}

	cancelAllRequest() {
		for (const [, controller] of this.abortControllerMap) {
			controller.controller.abort();
		}
		this.abortControllerMap.clear();
	}

	cancelRequest(url: string | string[]) {
		const urlList = Array.isArray(url) ? url : [url];
		for (const _url of urlList) {
			this.abortControllerMap.get(_url)?.controller.abort();
			this.abortControllerMap.delete(_url);
		}
	}

	private request<V = any>(config: IRequestConfig<V>) {
		if (config.interceptors?.requestSuccessFn) {
			config = config.interceptors.requestSuccessFn(config);
		}
		return new Promise<V>((resolve, reject) => {
			this.instance
				.request<any, V>(config)
				.then((res) => {
					if (config.interceptors?.responseSuccessFn)
						res = config.interceptors.responseSuccessFn(res);
					resolve(res);
				})
				.catch((err) => {
					if (config.interceptors?.responseFailureFn)
						err = config.interceptors.responseFailureFn(err);
					reject(err);
				});
		});
	}

	get<Res = any>(config: IRequestConfig<Res>) {
		return this.request<Res>({ ...config, method: 'GET' });
	}
	post<Res = any>(config: IRequestConfig<Res>) {
		return this.request<Res>({ ...config, method: 'POST' });
	}
	put<Res = any>(config: IRequestConfig<Res>) {
		return this.request<Res>({ ...config, method: 'PUT' });
	}
	patch<Res = any>(config: IRequestConfig<Res>) {
		return this.request<Res>({ ...config, method: 'PATCH' });
	}
	delete<Res = any>(config: IRequestConfig<Res>) {
		return this.request<Res>({ ...config, method: 'DELETE' });
	}
}

export default Request;
